package com.cy.pj.sys.service.aspect;

import com.cy.pj.common.annotation.RequiredLog;
import com.cy.pj.sys.pojo.SysLog;
import com.cy.pj.sys.service.SysLogService;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Date;

/**
 * Spring框架中由@Aspect注解描述的类型为切面类型，此切面类型中要有
 * 1)切入点的定义 (用于约束在哪些目标方法执行时，进行功能拓展)
 * 2)通知方法的定义(这样的方法中会封装要执行的扩展业务逻辑，例如日志记录)
 */

@Order(2)
@Slf4j
@Aspect
@Component
public class SysLogAspect {
    /**
     * @Pointcut注解用于定义切入点
     * @annotation(注解类全名) 为定义切入点的一种表达式，由表达式中注解描述
     * 的方法为切入点方法，可以在目标业务执行时通过指定的通知方法进行功能增强
     */
    @Pointcut ("@annotation(com.cy.pj.common.annotation.RequiredLog)")
    public void doLog(){}//此方法只负责承载切入点，不需要写方法实现

    /**
     * @Around 注解描述的方法为一个用于执行拓展业务逻辑的方法，此方法中
     * 可以通过连接点对象(joinPoint)调用目标方法。
     * @param joinPoint 表示一个连接点对象，此对象封装了切入点方法信息(目标方法信息)，
     *                  可以通过连接点对象调用切入点方法(目标方法)
     * @return 目标方法的返回结果。
     */
    @Around("doLog()")
    public Object doLogAround(ProceedingJoinPoint joinPoint)
            throws Throwable {
        System.out.println("SysLogAspect.@Around.before");
        long t1= System.currentTimeMillis();
        log.info("before {}",t1);
        try {
//            System.out.println("before:"+System.currentTimeMillis());
//            log.info("before {}",System.currentTimeMillis());
            Object result = joinPoint.proceed();//调用切入点方法
            long t2=System.currentTimeMillis();
//            System.out.println("after:"+System.currentTimeMillis());
            log.info("after {}",t2);//记录详细的正常行为日志
            doLogInfo(joinPoint,(t2-t1),null);
            return result;//此返回值会交给代理对象，代理对象传给调用方法
        }catch (Throwable e){
            e.printStackTrace();
            long t3=System.currentTimeMillis();
            log.error("exception {}",t3);
            //记录详细的异常行为日志
            doLogInfo(joinPoint,(t3-t1),e);
            throw e;
        }
    }
    private void doLogInfo(ProceedingJoinPoint joinPoint,long time,Throwable e)
            throws Throwable {
        String username="admin";
        String ip="202.106.29.20";
//        Long time=2L;

        Class<?> targetCls = joinPoint.getTarget().getClass();
        MethodSignature ms = (MethodSignature) joinPoint.getSignature();
        Method targetMethod = targetCls.getDeclaredMethod(ms.getName(), ms.getParameterTypes());
        RequiredLog requiredLog = targetMethod.getAnnotation(RequiredLog.class);
        String operation = requiredLog.operation();

        String targetClsMethod = targetCls.getName() + "." + targetMethod.getName();

        Object[] args=joinPoint.getArgs();
        String jsonParamStr = new ObjectMapper().writeValueAsString(args);


        SysLog userLog = new SysLog();
        userLog.setUsername(username);
        userLog.setIp(ip);
        userLog.setOperation(operation);
        userLog.setMethod(targetClsMethod);
        userLog.setParams(jsonParamStr);
        if(e!=null) {
            userLog.setStatus(0);
            userLog.setError(e.getMessage());
        }
        userLog.setTime(time);
        userLog.setCreatedTime(new Date());


        log.info("user.oper {}",new ObjectMapper().writeValueAsString(userLog));
        sysLogService.saveLog(userLog);

//        new Thread(){
//            @Override
//            public void run() {
//                sysLogService.saveLog(userLog);
//            }
//        }.start();


    }
    @Autowired
    private SysLogService sysLogService;
}
